<?php

declare(strict_types=1);

namespace app\service\org;

use app\model\RegionUser;
use think\facade\Db;

class OrgAuditService
{
    private const STATUS_LABELS = [
        0 => '待审核',
        1 => '已通过',
        2 => '已驳回',
    ];

    private array $regionManagerCache = [];

    private ?array $serviceOperatorIds = null;

    public function paginate(array $filters, int $page, int $pageSize, array $context = []): array
    {
        $records = array_merge(
            $this->fetchCompanyRecords($filters),
            $this->fetchIndividualRecords($filters)
        );

        $records = array_map(function (array $record): array {
            $record['assignment'] = $this->determineAssignment($record);
            return $record;
        }, $records);

        $records = $this->filterByOperator($records, $context);

        usort($records, function (array $a, array $b): int {
            $priorityA = $this->statusPriority((int) ($a['status'] ?? 0));
            $priorityB = $this->statusPriority((int) ($b['status'] ?? 0));
            if ($priorityA !== $priorityB) {
                return $priorityA <=> $priorityB;
            }

            $timeA = $a['submitted_at'] ?? '';
            $timeB = $b['submitted_at'] ?? '';
            return strcmp($timeB, $timeA);
        });

        $total = count($records);
        $offset = max(0, ($page - 1) * $pageSize);
        $slice = array_slice($records, $offset, $pageSize);

        return [
            'list' => $slice,
            'total' => $total,
        ];
    }

    public function countPending(array $context = []): int
    {
        $records = array_merge(
            $this->fetchCompanyRecords(['status' => 0]),
            $this->fetchIndividualRecords(['status' => 0])
        );

        if (!empty($context)) {
            $records = $this->filterByOperator($records, $context);
        }

        return count($records);
    }

    private function determineAssignment(array $record): array
    {
        $invitedBy = isset($record['invited_by']) ? (int) $record['invited_by'] : null;
        if ($invitedBy) {
            return [
                'owner_type' => 'inviter',
                'owner_ids' => [$invitedBy],
            ];
        }

        $region = $record['region'] ?? null;
        $regionId = is_array($region) && isset($region['id']) ? (int) $region['id'] : null;

        if ($regionId) {
            return [
                'owner_type' => 'region',
                'region_id' => $regionId,
                'owner_ids' => $this->resolveRegionManagerIds($regionId),
                'fallback' => 'service',
            ];
        }

        return [
            'owner_type' => 'service',
            'owner_ids' => $this->resolveServiceOperatorIds(),
            'fallback' => 'admin',
        ];
    }

    private function filterByOperator(array $records, array $context): array
    {
        $operatorId = (int) ($context['operator_id'] ?? 0);
        if ($operatorId <= 0) {
            return $records;
        }

        $isAdmin = $this->isAdminOperator($context);
        if ($isAdmin) {
            return $records;
        }

        $isRegionOperator = $this->isRegionOperator($context);
        $isServiceOperator = $this->isServiceOperator($context);

        [$regionIds, $regionPaths] = $this->resolveRegionScope($context);

        $filtered = [];
        foreach ($records as $record) {
            $assignment = $record['assignment'] ?? [];
            $invitedBy = isset($record['invited_by']) ? (int) $record['invited_by'] : null;
            $region = $record['region'] ?? null;
            $regionId = is_array($region) && isset($region['id']) ? (int) $region['id'] : null;
            $regionPath = is_array($region) && isset($region['path']) ? (string) $region['path'] : null;

            $canView = false;

            if ($invitedBy && $invitedBy === $operatorId) {
                $canView = true;
            } elseif ($isRegionOperator && ($assignment['owner_type'] ?? '') === 'region') {
                if ($regionId && $this->regionMatches($regionId, $regionPath, $regionIds, $regionPaths)) {
                    $canView = true;
                }
            } elseif ($isServiceOperator && ($assignment['owner_type'] ?? '') === 'service') {
                $canView = true;
            }

            if (!$canView && !$isRegionOperator && !$isServiceOperator) {
                // 默认允许查看自己邀请的记录
                $canView = $invitedBy && $invitedBy === $operatorId;
            }

            if ($canView) {
                $filtered[] = $record;
            }
        }

        return $filtered;
    }

    private function resolveRegionScope(array $context): array
    {
        $scope = $context['region_scope'] ?? [];
        $ids = [];
        $paths = [];

        if (isset($scope['ids']) && is_array($scope['ids'])) {
            $ids = array_values(array_unique(array_map('intval', $scope['ids'])));
        }

        if (isset($scope['paths']) && is_array($scope['paths'])) {
            foreach ($scope['paths'] as $id => $path) {
                $paths[(int) $id] = (string) $path;
            }
        }

        return [$ids, $paths];
    }

    private function regionMatches(?int $regionId, ?string $regionPath, array $allowedIds, array $allowedPaths): bool
    {
        if ($regionId && in_array($regionId, $allowedIds, true)) {
            return true;
        }

        if ($regionPath !== null && $regionPath !== '') {
            foreach ($allowedIds as $allowedId) {
                $needle = '/' . $allowedId . '/';
                if (str_contains($regionPath, $needle)) {
                    return true;
                }
            }
        }

        if ($regionId && isset($allowedPaths[$regionId])) {
            return true;
        }

        return false;
    }

    private function isAdminOperator(array $context): bool
    {
        $roleSlugs = isset($context['role_slugs']) && is_array($context['role_slugs'])
            ? array_map('strtolower', $context['role_slugs'])
            : [];
        $identities = isset($context['identities']) && is_array($context['identities'])
            ? $context['identities']
            : [];

        $adminRoles = ['admin', 'system_admin'];

        foreach ($roleSlugs as $slug) {
            if (in_array($slug, $adminRoles, true)) {
                return true;
            }
        }

        foreach ($identities as $identity) {
            $type = strtolower((string) ($identity['type'] ?? ''));
            $status = (int) ($identity['status'] ?? 0);
            if ($status === 1 && in_array($type, ['admin', 'operator'], true)) {
                return true;
            }
        }

        return false;
    }

    private function isRegionOperator(array $context): bool
    {
        $roleSlugs = isset($context['role_slugs']) && is_array($context['role_slugs'])
            ? array_map('strtolower', $context['role_slugs'])
            : [];
        $identities = isset($context['identities']) && is_array($context['identities'])
            ? $context['identities']
            : [];

        $regionRoles = ['region_manager', 'region_operator'];

        foreach ($roleSlugs as $slug) {
            if (in_array($slug, $regionRoles, true)) {
                return true;
            }
        }

        foreach ($identities as $identity) {
            $type = strtolower((string) ($identity['type'] ?? ''));
            $status = (int) ($identity['status'] ?? 0);
            if ($status === 1 && in_array($type, $regionRoles, true)) {
                return true;
            }
        }

        return false;
    }

    private function isServiceOperator(array $context): bool
    {
        $roleSlugs = isset($context['role_slugs']) && is_array($context['role_slugs'])
            ? array_map('strtolower', $context['role_slugs'])
            : [];
        $identities = isset($context['identities']) && is_array($context['identities'])
            ? $context['identities']
            : [];

        $serviceRoles = ['support'];

        foreach ($roleSlugs as $slug) {
            if (in_array($slug, $serviceRoles, true)) {
                return true;
            }
        }

        foreach ($identities as $identity) {
            $type = strtolower((string) ($identity['type'] ?? ''));
            $status = (int) ($identity['status'] ?? 0);
            if ($status === 1 && in_array($type, ['support', 'service_operator'], true)) {
                return true;
            }
        }

        return false;
    }

    private function statusPriority(int $status): int
    {
        return match ($status) {
            0 => 0,
            1 => 1,
            2 => 2,
            default => 3,
        };
    }

    private function resolveRegionManagerIds(int $regionId): array
    {
        if (!array_key_exists($regionId, $this->regionManagerCache)) {
            $ids = Db::name('region_user')
                ->where('region_id', $regionId)
                ->where('status', 1)
                ->where('role_type', RegionUser::ROLE_MANAGER)
                ->column('user_id') ?: [];

            $this->regionManagerCache[$regionId] = array_values(array_unique(array_map('intval', $ids)));
        }

        return $this->regionManagerCache[$regionId];
    }

    private function resolveServiceOperatorIds(): array
    {
        if ($this->serviceOperatorIds === null) {
            $ids = Db::name('auth_user_role')
                ->alias('aur')
                ->join('auth_role ar', 'ar.id = aur.role_id')
                ->where('ar.slug', 'support')
                ->where('aur.status', 1)
                ->whereNull('aur.delete_time')
                ->column('aur.user_id') ?: [];

            $this->serviceOperatorIds = array_values(array_unique(array_map('intval', $ids)));
        }

        return $this->serviceOperatorIds;
    }

    private function formatInviter(array $row, string $prefix): ?array
    {
        $idKey = $prefix . '_id';
        $phoneKey = $prefix . '_phone';
        $nicknameKey = $prefix . '_nickname';
        $realNameKey = $prefix . '_real_name';

        if (!isset($row[$idKey]) || !$row[$idKey]) {
            return null;
        }

        return [
            'id' => (int) $row[$idKey],
            'phone' => $row[$phoneKey] ?? '',
            'nickname' => $row[$nicknameKey] ?? '',
            'real_name' => $row[$realNameKey] ?? '',
        ];
    }

    private function fetchCompanyRecords(array $filters): array
    {
        $typeFilter = (string) ($filters['org_type'] ?? '');
        if ($typeFilter !== '' && $typeFilter !== 'seller' && $typeFilter !== 'both') {
            return [];
        }

        $query = Db::name('user_company')
            ->alias('uc')
            ->leftJoin('user_account ua', 'ua.id = uc.user_id')
            ->leftJoin('user_profile up', 'up.user_id = uc.user_id')
            ->leftJoin('user_identity si', "si.user_id = uc.user_id AND si.identity_type = 'seller'")
            ->leftJoin('org o', 'o.id = si.org_id')
            ->leftJoin('region r', 'r.id = IFNULL(o.region_id, si.region_id)')
            ->leftJoin('user_account inviter', 'inviter.id = IFNULL(o.inviter_user_id, IFNULL(si.invited_by, ua.invited_by))')
            ->leftJoin('user_profile inviter_profile', 'inviter_profile.user_id = inviter.id');

        $keyword = trim((string) ($filters['keyword'] ?? ''));
        if ($keyword !== '') {
            $like = '%' . addcslashes($keyword, '%_') . '%';
            $query->where(function ($q) use ($like) {
                $q->whereLike(
                    'uc.company_name|uc.license_code|ua.phone|up.nickname|up.real_name',
                    $like
                );
            });
        }

        if (array_key_exists('status', $filters) && $filters['status'] !== '' && $filters['status'] !== null) {
            $query->where('uc.status', (int) $filters['status']);
        }

        $regionId = isset($filters['region_id']) && $filters['region_id'] !== ''
            ? (int) $filters['region_id']
            : null;
        if ($regionId) {
            $query->where(function ($q) use ($regionId) {
                $q->where('o.region_id', '=', $regionId)
                    ->whereOr('si.region_id', '=', $regionId);
            });
        }

        $rows = $query->field([
            'uc.id as audit_id',
            'uc.user_id',
            'uc.company_name',
            'uc.license_code',
            'uc.license_photo',
            'uc.legal_rep',
            'uc.address',
            'uc.scope',
            'uc.region_id as company_region_id',
            'uc.extra',
            'uc.ocr_raw',
            'uc.status',
            'uc.verified_at as verified_time',
            'uc.submitted_at',
            'uc.create_time',
            'ua.phone',
            'ua.invited_by as account_invited_by',
            'up.nickname',
            'up.real_name',
            'o.id as org_id',
            'o.name as org_name',
            'o.region_id as org_region_id',
            'si.region_id as identity_region_id',
            'IFNULL(o.inviter_user_id, IFNULL(si.invited_by, ua.invited_by)) as invited_by',
            'inviter.id as inviter_id',
            'inviter.phone as inviter_phone',
            'inviter_profile.nickname as inviter_nickname',
            'inviter_profile.real_name as inviter_real_name',
            'r.name as region_name',
            'r.path as region_path'
        ])->select()->toArray();

        $records = [];
        foreach ($rows as $row) {
            $regionId = $row['org_region_id'] ?? ($row['identity_region_id'] ?? $row['company_region_id'] ?? null);
            $extra = [];
            if (!empty($row['extra'])) {
                $decoded = json_decode($row['extra'], true);
                if (is_array($decoded)) {
                    $extra = $decoded;
                }
            }

            $invoice = $extra['invoice'] ?? null;
            $addresses = $extra['addresses'] ?? [];
            $normalizedAddresses = [];
            if (is_array($addresses)) {
                foreach ($addresses as $address) {
                    if (!is_array($address)) {
                        continue;
                    }
                    $normalizedAddresses[] = [
                        'address_type' => $address['address_type'] ?? 'shipping',
                        'contact_name' => $address['contact_name'] ?? '',
                        'contact_phone' => $address['contact_phone'] ?? '',
                        'province' => $address['province'] ?? '',
                        'city' => $address['city'] ?? '',
                        'district' => $address['district'] ?? '',
                        'address' => $address['address'] ?? '',
                        'postal_code' => $address['postal_code'] ?? '',
                        'is_default' => isset($address['is_default']) ? (int) $address['is_default'] : 0,
                    ];
                }
            }
            $invitedBy = isset($row['invited_by']) && $row['invited_by'] !== null
                ? (int) $row['invited_by']
                : null;

            $records[] = [
                'audit_id' => (int) $row['audit_id'],
                'user_id' => (int) $row['user_id'],
                'type' => 'company',
                'type_label' => '企业认证',
                'org_type' => 'seller',
                'status' => (int) $row['status'],
                'status_label' => $this->statusLabel((int) $row['status']),
                'display_name' => $row['company_name'] ?? '',
                'phone' => $row['phone'] ?? '',
                'invited_by' => $invitedBy,
                'inviter' => $this->formatInviter($row, 'inviter'),
                'profile' => [
                    'nickname' => $row['nickname'] ?? '',
                    'real_name' => $row['real_name'] ?? '',
                ],
                'org' => [
                    'id' => isset($row['org_id']) ? (int) $row['org_id'] : null,
                    'name' => $row['org_name'] ?? null,
                ],
                'region' => $regionId ? [
                    'id' => (int) $regionId,
                    'name' => $row['region_name'] ?? '',
                    'path' => $row['region_path'] ?? '',
                ] : null,
                'submitted_at' => $row['submitted_at'] ?? $row['create_time'] ?? null,
                'verified_time' => $row['verified_time'] ?? null,
                'company' => [
                    'company_name' => $row['company_name'] ?? '',
                    'license_code' => $row['license_code'] ?? '',
                    'legal_rep' => $row['legal_rep'] ?? '',
                    'address' => $row['address'] ?? '',
                    'scope' => $row['scope'] ?? null,
                    'license_photo' => $row['license_photo'] ?? '',
                    'invoice' => $invoice,
                    'addresses' => $normalizedAddresses,
                    'ocr_raw' => !empty($row['ocr_raw']) ? json_decode((string) $row['ocr_raw'], true) : null,
                ],
            ];
        }

        return $records;
    }

    private function fetchIndividualRecords(array $filters): array
    {
        $typeFilter = (string) ($filters['org_type'] ?? '');
        if ($typeFilter !== '' && $typeFilter !== 'buyer' && $typeFilter !== 'both') {
            return [];
        }

        $query = Db::name('user_individual')
            ->alias('ui')
            ->leftJoin('user_account ua', 'ua.id = ui.user_id')
            ->leftJoin('user_profile up', 'up.user_id = ui.user_id')
            ->leftJoin('user_identity bi', "bi.user_id = ui.user_id AND bi.identity_type = 'buyer'")
            ->leftJoin('org o', 'o.id = bi.org_id')
            ->leftJoin('region r', 'r.id = IFNULL(o.region_id, bi.region_id)')
            ->leftJoin('user_account inviter', 'inviter.id = IFNULL(o.inviter_user_id, IFNULL(bi.invited_by, ua.invited_by))')
            ->leftJoin('user_profile inviter_profile', 'inviter_profile.user_id = inviter.id');

        $keyword = trim((string) ($filters['keyword'] ?? ''));
        if ($keyword !== '') {
            $like = '%' . addcslashes($keyword, '%_') . '%';
            $query->where(function ($q) use ($like) {
                $q->whereLike('ui.real_name|ui.id_card|ua.phone|up.nickname', $like);
            });
        }

        if (array_key_exists('status', $filters) && $filters['status'] !== '' && $filters['status'] !== null) {
            $query->where('ui.status', (int) $filters['status']);
        }

        $regionId = isset($filters['region_id']) && $filters['region_id'] !== ''
            ? (int) $filters['region_id']
            : null;
        if ($regionId) {
            $query->where(function ($q) use ($regionId) {
                $q->where('o.region_id', '=', $regionId)
                    ->whereOr('bi.region_id', '=', $regionId);
            });
        }

        $rows = $query->field([
            'ui.id as audit_id',
            'ui.user_id',
            'ui.real_name',
            'ui.id_card',
            'ui.id_card_front',
            'ui.id_card_back',
            'ui.valid_from',
            'ui.valid_to',
            'ui.status',
            'ui.verified_at as verified_time',
            'ui.submitted_at',
            'ui.create_time',
            'ua.phone',
            'ua.invited_by as account_invited_by',
            'up.nickname',
            'up.real_name as profile_real_name',
            'o.id as org_id',
            'o.name as org_name',
            'o.region_id as org_region_id',
            'bi.region_id as identity_region_id',
            'IFNULL(o.inviter_user_id, IFNULL(bi.invited_by, ua.invited_by)) as invited_by',
            'inviter.id as inviter_id',
            'inviter.phone as inviter_phone',
            'inviter_profile.nickname as inviter_nickname',
            'inviter_profile.real_name as inviter_real_name',
            'r.name as region_name',
            'r.path as region_path'
        ])->select()->toArray();

        $records = [];
        foreach ($rows as $row) {
            $regionId = $row['org_region_id'] ?? ($row['identity_region_id'] ?? null);
            $invitedBy = isset($row['invited_by']) && $row['invited_by'] !== null
                ? (int) $row['invited_by']
                : null;
            $records[] = [
                'audit_id' => (int) $row['audit_id'],
                'user_id' => (int) $row['user_id'],
                'type' => 'individual',
                'type_label' => '个人认证',
                'org_type' => 'buyer',
                'status' => (int) $row['status'],
                'status_label' => $this->statusLabel((int) $row['status']),
                'display_name' => $row['real_name'] ?? '',
                'phone' => $row['phone'] ?? '',
                'invited_by' => $invitedBy,
                'inviter' => $this->formatInviter($row, 'inviter'),
                'profile' => [
                    'nickname' => $row['nickname'] ?? '',
                    'real_name' => $row['profile_real_name'] ?? '',
                ],
                'org' => [
                    'id' => isset($row['org_id']) ? (int) $row['org_id'] : null,
                    'name' => $row['org_name'] ?? null,
                ],
                'region' => $regionId ? [
                    'id' => (int) $regionId,
                    'name' => $row['region_name'] ?? '',
                    'path' => $row['region_path'] ?? '',
                ] : null,
                'submitted_at' => $row['submitted_at'] ?? $row['create_time'] ?? null,
                'verified_time' => $row['verified_time'] ?? null,
                'individual' => [
                    'real_name' => $row['real_name'] ?? '',
                    'id_card' => $row['id_card'] ?? '',
                    'id_card_front' => $row['id_card_front'] ?? '',
                    'id_card_back' => $row['id_card_back'] ?? '',
                    'valid_from' => $row['valid_from'] ?? null,
                    'valid_to' => $row['valid_to'] ?? null,
                ],
            ];
        }

        return $records;
    }

    private function statusLabel(int $status): string
    {
        return self::STATUS_LABELS[$status] ?? '未知状态';
    }
}
